﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Xceed.Silverlight.Data;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Collections;
using System.Collections.Specialized;

namespace Xceed.Silverlight.DataGrid
{
  public class DataGridPath
  {
    internal DataGridPath( DataPath sourcePath )
    {
      if( sourcePath == null )
        throw new ArgumentNullException( "sourcePath" );

      m_sourceDataPath = sourcePath;
    }

    public DataGridPath( object[] path )
    {
      if( path == null )
        throw new ArgumentNullException( "path" );

      m_sourceArray = path;
    }

    public DataGridPath()
    {
      ObservableCollection<object> collection = new ObservableCollection<object>();
      collection.CollectionChanged += OnCollectionChanged;

      m_sourceList = collection;
    }

    #region DataPath Property

    public IList<Object> DataPath
    {
      get
      {
        if( m_sourceList == null )
        {
          if( m_sourceArray == null )
          {
            this.GenerateSourceArrayFromDataPath();
          }

          Debug.Assert( m_sourceArray != null );

          m_sourceList = new ReadOnlyListArrayWrapper<Object>( m_sourceArray );
        }

        return m_sourceList;
      }
    }

    #endregion

    #region ActualDataPath Property

    internal DataPath ActualDataPath
    {
      get
      {
        return m_sourceDataPath;
      }
      set
      {
        if( value != m_sourceDataPath )
        {
          m_sourceDataPath = value;
        }
      }
    }

    #endregion

    #region ParentPath Property

    public DataGridPath ParentPath
    {
      get
      {
        if( ( m_parentPath == null ) && ( this.DataPath.Count > 1 ) )
        {
          if( m_sourceDataPath != null )
          {
            m_parentPath = new DataGridPath( m_sourceDataPath.ParentPath );
          }
          else
          {
            int depth =  this.DataPath.Count - 1;
            object[] array = new object[ depth ];

            for( int i = 0; i < depth; i++ )
            {
              array[ i ] = this.DataPath[ i ];
            }

            m_parentPath = new DataGridPath( array );
          }
        }

        return m_parentPath;
      }
    }

    DataGridPath m_parentPath;

    #endregion

    internal bool IsLeafLoaded()
    {
      return this.ActualDataPath.LastChild.IsLoaded;
    }

    private void GenerateSourceArrayFromDataPath()
    {
      Debug.Assert( m_sourceDataPath != null );

      int length = m_sourceDataPath.Depth;
      m_sourceArray = new object[ length ];

      for( int i = 0; i < length; i++ )
      {
        m_sourceArray[ i ] = m_sourceDataPath[ i ].Data;
      }
    }

    private void OnCollectionChanged( object sender, NotifyCollectionChangedEventArgs e )
    {
      //the DataPath collection was changed by the "user". Reset the "ParentPath" storage.
      //Note: This can occur only when using the CTOR that takes nothing in parameter.

      m_parentPath = null;
    }

    public override int GetHashCode()
    {
      IList<object> sourceList = this.DataPath;

      int hash = 0;

      int count = sourceList.Count;

      if( count > 0 )
      {
        hash = sourceList[ 0 ].GetHashCode();

        for( int i = 1; i < count; i++ )
        {
          hash &= sourceList[ i ].GetHashCode();
        }
      }

      return hash;
    }

    public override bool Equals( object obj )
    {
      DataGridPath pathObj = obj as DataGridPath;

      if( pathObj == null )
        return false;

      return this.Equals( pathObj );
    }

    public bool Equals( DataGridPath path )
    {
      DataGridPath dataGridPath = path;
      if( dataGridPath == null )
        return false;

      DataItem dataGridItem = dataGridPath.ActualDataPath.LastChild;

      //If the last child of the element passed in param is not loaded, then equality is not possible.
      if( ( dataGridItem == null ) || ( !dataGridItem.IsLoaded ) )
        return false;

      //If the last child of the current element is not loaded, then equality is not possible.
      dataGridItem = this.ActualDataPath.LastChild;

      if( ( dataGridItem == null ) || ( !dataGridItem.IsLoaded ) )
        return false;

      IList<object> sourceList = this.DataPath;
      IList<object> referenceList = dataGridPath.DataPath;

      int count = sourceList.Count;

      if( count != referenceList.Count )
        return false;

      for( int i = 0; i < count; i++ )
      {
        if( !object.Equals( sourceList[ i ], referenceList[ i ] ) )
          return false;
      }

      return true;
    }

    private DataPath m_sourceDataPath;
    private object[] m_sourceArray;
    private IList<Object> m_sourceList;
  }
}
